/*
 * TepcoMeter:GraphView.java
 * Copyright © 2011 ho9ho9 All Rights Reserved.
 */
package com.ho9ho9.tepcometer.view;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.graphics.Paint.Style;
import android.graphics.Shader.TileMode;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import com.ho9ho9.tepcometer.dto.HourDto;
import com.ho9ho9.tepcometer.dto.WattDto;

/**
 * GraphViewクラス.
 * 
 * @author ho9ho9
 */
public class GraphView extends SurfaceView implements SurfaceHolder.Callback {
	private static final float MEMORY_WIDTH = 2.0f;
	private static final float FRAME_WIDTH = 2.0f;
	private static final String Y_UNIT = "(万KW)";
	private static final String X_UNIT = "(時台)";

	private static final String TAG_NAME = "GraphView";
	
	private WattDto wattDto;
	
	public GraphView(Context context) {
		super(context);
		// SurfaceView描画に用いるコールバック登録
		getHolder().addCallback(this);
	}

	private void doDraw() {
		// Canvasクラス取得
		Canvas canvas = getHolder().lockCanvas();
		// Paintクラス作成
		// 画面サイズ取得
		int viewWidth = getWidth();
		int viewHeight = getHeight();

		// 状態を保存
		canvas.save();

		// 背景色設定
		canvas.drawColor(Color.BLACK);
		
		// 枠の描画
		Paint framePaint = new Paint();
		framePaint.setStrokeWidth(FRAME_WIDTH);
		framePaint.setAntiAlias(true);
		framePaint.setColor(Color.WHITE);
		// 座標計算
		float startFrameX = subtraction(viewWidth, 0.11f);
		float startFrameY = subtraction(viewHeight, 0.90f);
		float endFrameX = subtraction(viewWidth, 0.97f);
		float endFrameY = subtraction(viewHeight, 0.08f);
		// x軸、y軸
		canvas.drawLine(startFrameX, startFrameY, endFrameX, startFrameY, framePaint);
		canvas.drawLine(startFrameX, startFrameY, startFrameX, endFrameY, framePaint);
		
		// Y軸 メモリ描画
		// 最大値計算
		int maxWatt = (int)(Math.ceil((float)wattDto.getCapability() / 1000f) * 1000);
		// 開始Xのメモリ位置計算
		float startMemX = startFrameX - subtraction(viewWidth, 0.01f);
		// メモリ幅計算
		float maxMemY = subtraction(viewHeight, 0.12f);
		int memCount = maxWatt / 1000;
		float memYInc = (startFrameY - maxMemY) / (float)memCount;
		float[] memYArr = new float[memCount];
		// メモリ位置計算
		for (int i = 0; i < memYArr.length; i++) {
			memYArr[i] = startFrameY - (memYInc * (i + 1));
		}
		// 描画
		framePaint.setStrokeWidth(MEMORY_WIDTH);
		for (float y : memYArr) {
			Log.d(TAG_NAME, "★" + startMemX + ":" + y + ":" + startFrameX + ":" + y);
			canvas.drawLine(startMemX, y, startFrameX, y, framePaint);
		}
		// Y軸ラベル描画
		// テキスト用ペイントの生成
		Paint textPaintY = new Paint(Paint.ANTI_ALIAS_FLAG);
		textPaintY.setColor(Color.WHITE);
		// テキストの大きさ
		textPaintY.setTextSize(subtraction(viewWidth, 0.03f));
		// X軸の中心点計算
		float memCenterX = subtraction(viewWidth, 0.05f);
		// 描画
		NumberFormat nf = new DecimalFormat("#,###,###");
		for (int i = 0; i < memYArr.length; i++) {
			String drawText = nf.format((i + 1) * 1000);
			float baseX = calculateDrawTextX(memCenterX, drawText, textPaintY);
			float baseY = calculateDrawTextY(memYArr[i], textPaintY);
			// テキストの描画
			canvas.drawText(drawText, baseX, baseY, textPaintY);
		}
		// 単位描画
		Paint unitPaintY = new Paint(Paint.ANTI_ALIAS_FLAG);
		unitPaintY.setColor(Color.WHITE);
		unitPaintY.setTextSize(subtraction(viewWidth, 0.025f));
		canvas.drawText(Y_UNIT, 
				calculateDrawTextX(memCenterX, Y_UNIT, unitPaintY), 
				calculateDrawTextY(subtraction(viewHeight, 0.035f), unitPaintY), unitPaintY);
		
		// X軸 メモリ描画
		// 最大値計算
		// 開始Yのメモリ位置計算
		float startMemY = startFrameY + subtraction(viewHeight, 0.01f);
		// メモリ幅計算
		float maxMemX = subtraction(viewWidth, 0.95f);
		float memXInc = (maxMemX - startFrameX) / (float)wattDto.getHours().size();
		float[] memXArr = new float[wattDto.getHours().size()];
		// メモリ位置計算
		for (int i = 0; i < memXArr.length; i++) {
			memXArr[i] = startFrameX + (memXInc * (i + 1));
		}
		// 描画
		framePaint.setStrokeWidth(MEMORY_WIDTH);
		for (float x : memXArr) {
			Log.d(TAG_NAME, "◇" + x + ":" + startMemY + ":" + x + ":" + startFrameY);
			canvas.drawLine(x, startMemY, x, startFrameY, framePaint);
		}
		// X軸ラベル描画
		// テキスト用ペイントの生成
		Paint textPaintX = new Paint(Paint.ANTI_ALIAS_FLAG);
		textPaintX.setColor(Color.WHITE);
		// テキストの大きさ
		textPaintX.setTextSize(subtraction(viewWidth, 0.025f));
		// Y軸の中心点計算
		float memCenterY = subtraction(viewHeight, 0.935f);
		// 描画
		FontMetrics fontMetricsX = textPaintX.getFontMetrics();
		for (int i = 0; i < memXArr.length; i++) {
			String drawText = Integer.toString(wattDto.getHours().get(i).getWattHour());
			// 文字列の幅を取得
			float textWidth = textPaintX.measureText(drawText);
			// 中心にしたいX座標から文字列の幅の半分を引く
			float baseX = memXArr[i] - (memXInc / 2f) - textWidth / 2f;
			// 中心にしたいY座標からAscentとDescentの半分を引く
			float baseY = memCenterY - (fontMetricsX.ascent + fontMetricsX.descent) / 2;
			// テキストの描画
			canvas.drawText(drawText, baseX, baseY, textPaintX);
		}
		// 単位描画
		Paint unitPaintX = new Paint(Paint.ANTI_ALIAS_FLAG);
		unitPaintX.setColor(Color.WHITE);
		unitPaintX.setTextSize(subtraction(viewWidth, 0.02f));
		canvas.drawText(X_UNIT, 
				calculateDrawTextX(subtraction(viewWidth, 0.97f), X_UNIT, unitPaintX), 
				calculateDrawTextY(subtraction(viewHeight, 0.985f), unitPaintX), unitPaintX);
		
		// 当日最大電力量
		Paint maxWattPaint = new Paint();
		maxWattPaint.setStrokeWidth(2.0f);
		maxWattPaint.setColor(Color.rgb(0xfe, 0x52, 0x43));
		maxWattPaint.setAlpha(150);
		float maxWattY = startFrameY - ((float)wattDto.getCapability() * (memYInc / 1000f));
		canvas.drawLine(startFrameX + 0.5f, maxWattY, endFrameX, maxWattY, maxWattPaint);
		
		// グラフを描画
		Paint graphFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		Paint graphStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		graphStrokePaint.setStyle(Style.STROKE);
		graphStrokePaint.setColor(Color.GRAY);
		List<HourDto> hourDtos = wattDto.getHours();
		for (int i = 0; i < hourDtos.size(); i++) {
			int hourWatt = hourDtos.get(i).getWattValue();
			if (hourWatt > 0) {
				Integer calcRet = calculateUseWatt(hourWatt, wattDto.getCapability());
				float graphTop = startFrameY - ((float)hourWatt * (memYInc / 1000f));
				float graphLeft = memXArr[i] - memXInc;
				float graphRight = memXArr[i];
				LinearGradient gr = new LinearGradient(
						graphLeft, graphTop, graphLeft, startFrameY - FRAME_WIDTH,
						getWattMetterGradientColor(calcRet), null, TileMode.CLAMP);
				graphFillPaint.setShader(gr);
				canvas.drawRect(graphLeft, graphTop, graphRight, startFrameY - 0.5f, graphFillPaint);
				canvas.drawRect(graphLeft, graphTop, graphRight, startFrameY - 0.5f, graphStrokePaint);
			}
		}
		
		// 補助線
		Paint assistanceLinePaint = new Paint();
		assistanceLinePaint.setColor(Color.WHITE);
		assistanceLinePaint.setAlpha(200);
		assistanceLinePaint.setStrokeWidth(0.5f);
		float assistanceLineStartX = startFrameX + 1.0f;
		for (float y : memYArr) {
			int dotMax = (int)(endFrameX - assistanceLineStartX);
			for (int i = 0; i < dotMax; i++) {
				if (i % 2 == 0) {
					float start = assistanceLineStartX + (float)i;
					float end = assistanceLineStartX + (float)i + 1.0f;
					canvas.drawLine(start, y, end, y, assistanceLinePaint);
				}
			}
		}
		// 状態を復元
		canvas.restore();
		// 描画を終了
		getHolder().unlockCanvasAndPost(canvas);
	}

	private float calculateDrawTextY(float centerY, Paint paint) {
		FontMetrics fontMetricsY = paint.getFontMetrics();
		// 中心にしたいY座標からAscentとDescentの半分を引く
		return centerY - (fontMetricsY.ascent + fontMetricsY.descent) / 2;
	}

	private float calculateDrawTextX(float centerX, String drawText, Paint paint) {
		// 文字列の幅を取得
		float textWidth = paint.measureText(drawText);
		// 中心にしたいX座標から文字列の幅の半分を引く
		return centerX - textWidth / 2f;
	}

	private Integer calculateUseWatt(Integer currentWatt, Integer capability) {
		Float calcRet = (new Float(currentWatt) / new Float(capability)) * 100;
		return Math.round(calcRet);
	}

	private int[] getWattMetterGradientColor(Integer calcRet) {
		int[] result = null;
		if (calcRet < 90) {
			result = new int[] {0xff2df86e, 0xff8ffbb2};
		} else if (calcRet < 96) {
			result = new int[] {0xffffe32a, 0xfffff3a1};
		} else {
			result = new int[] {0xffff2257, 0xffff99b1};
		}
		return result;
	}
	
	private float subtraction(int i, float f) {
		return (float)i * f;
	}

	// SurfaceViewが最初に生成されたときに呼び出される
	public void surfaceCreated(SurfaceHolder holder) {
		Log.d(getClass().getName(), "surfaceCreated");
		// 描画
		doDraw();
	}

	// SurfaceViewのサイズなどが変更されたときに呼び出される
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		Log.d(getClass().getName(), "surfaceChanged");
	}

	// SurfaceViewが破棄されるときに呼び出される
	public void surfaceDestroyed(SurfaceHolder holder) {
		Log.d(getClass().getName(), "surfaceDestroyed");
	}
	
	public WattDto getWattDto() {
		return wattDto;
	}

	public void setWattDto(WattDto wattDto) {
		this.wattDto = wattDto;
	}

}
